//    Copyright (C) Kherty.  All rights reserved.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics.Contracts;
using System.Reflection;
using OpenLS.Chart.Internals;
using OpenLS.Core.Notification;

namespace OpenLS.Chart.UI.Internals
{
    public class SeriesObserver<SeriesType> : ViewModelBase where SeriesType : INumericValuesProvider
    {
        public SeriesObserver(ObservableCollection<SeriesType> series, BarGrouping grouping)
        {
            Grouping = grouping;
            _series = series;
            foreach (SeriesType s in series)
                registerSeries(s);
            this.IsBarType = typeof (BarSeries).IsAssignableFrom(typeof (SeriesType));
            series.CollectionChanged += series_CollectionChanged;
        }

        private BarGrouping _grouping;

        public BarGrouping Grouping
        {
            get { return _grouping; }
            set { _grouping = value; Generation++; }
        }
	
        private double? _minimum;

        public double? ActualMinimum
        {
            get
            {
                var result = Minimum;
                if (Grouping == BarGrouping.Stacked)
                {
                    return Math.Min(0, result.GetValueOrDefault());
                }
                return result;
            }
        }
        public double? Minimum
        {
            get
            {
                if (_minimum == null)
                    calculateMinMax();
                return _minimum;
            }
            private set { _minimum = value; notifyPropertyChanged("Minimum"); notifyPropertyChanged("ActualMinimum"); }
        }
        private double? _Maximum;

        public double? Maximum
        {
            get
            {
                if (_Maximum == null)
                    calculateMinMax();
                return _Maximum;
            }
            set { _Maximum = value; notifyPropertyChanged("Maximum"); }
        }

        void calculateMinMax()
        {
            switch (Grouping)
            {
                case BarGrouping.Standard:
                case BarGrouping.Clustered:
                    calculateClustered();
                    break;
                case BarGrouping.Stacked:
                    calculateStacked();
                    break;
                case BarGrouping.PercentStacked:
                    calculatePercentStacked();
                    break;
                default:
                    throw new NotSupportedException();
            }
        }

        private void calculatePercentStacked()
        {
            var positives = new Dictionary<int, double>();
            var negatives = new Dictionary<int, double>();
            foreach (SeriesType ser in _series)
            {
                foreach (NumericValue v in ser.GetValues())
                {
                    if (!negatives.ContainsKey(v.Index))
                    {
                        if (v.Value > 0)
                        {
                            negatives.Add(v.Index, 0);
                            positives.Add(v.Index, v.Value);
                        }
                        else
                        {
                            negatives.Add(v.Index, -v.Value);
                            positives.Add(v.Index, 0);
                        }
                        continue;
                    }
                    if (v.Value > 0)
                    {
                        positives[v.Index] += v.Value;
                    }
                    else
                    {
                        negatives[v.Index] += -v.Value;
                    }
                }
                if (negatives.Count == 0)
                    return;


            }
            const double factor = 1;
            Maximum = 1 * factor;
            Minimum = 0;
            if (IsBarType)
                foreach (int i in positives.Keys)
                {
                    Minimum = Math.Min(Minimum.Value, -negatives[i]/positives[i]*factor);
                }

        }

        private void calculateClustered()
        {
            double min = double.MaxValue;
            double max = double.MinValue;
            foreach (SeriesType ser in _series)
            {
                foreach (NumericValue v in ser.GetValues())
                {
                    min = Math.Min(min, v.Value);
                    max = Math.Max(max, v.Value);
                }
            }
            if (min != double.MaxValue)
            {
                Maximum = max;
                Minimum = min;
            }
        }

        private void calculateStacked()
        {
            var mins = new Dictionary<int, double>();
            var maxes = new Dictionary<int, double>();
            foreach (SeriesType ser in _series)
            {
                foreach (NumericValue v in ser.GetValues())
                {
                    if (!maxes.ContainsKey(v.Index))
                    {
                        maxes.Add(v.Index, Math.Max(v.Value, 0));
                        mins.Add(v.Index, Math.Min(v.Value, 0));
                        continue;
                    }
                    maxes[v.Index] += Math.Max(v.Value, 0);
                    mins[v.Index] += Math.Min(v.Value, 0);
                }
                if (maxes.Count == 0)
                    return;
                double max = double.MinValue;
                foreach (double d in maxes.Values)
                {
                    max = Math.Max(max,d );
                }
                double min = double.MaxValue;
                foreach (double d in mins.Values)
                {
                    min = Math.Min(min, d);
                }
                Minimum = min;
                if (!IsBarType)
                    Minimum = 0;
                Maximum = max;


            }
        }
                    

           
        



        void series_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.NewItems != null)
                foreach (SeriesType s in e.NewItems)
                {
                    registerSeries(s);
                }
            if (e.OldItems != null)
                foreach (SeriesType s in e.OldItems)
                {
                    s.ValueChanged -= onValueChanged;
                }
        }
        public int MaxIndex
        {
            get
            {
                int result = 0;
                foreach (SeriesType s in _series)
                    result = Math.Max(result, getCount(s));
                return result;
            }
        }

        private static int getCount(SeriesType s)
        {
            int result = 0;
            foreach (var o in s.GetValues())
                result = Math.Max(result, o.Index);
            return result;
        }

        private void registerSeries(SeriesType s)
        {
            s.ValueChanged += onValueChanged;
            notifyPropertyChanged("MaxIndex");
            // do something for count
        }

        void onValueChanged(object sender, EventArgs e)
        {
            Generation++;
            Maximum = null;
            Minimum = null;
        }

        readonly ObservableCollection<SeriesType> _series;

        private int _generation;
        private bool IsBarType;

        public int Generation
        {
            get { return _generation; }
            set
            {
                _generation = value;
                notifyPropertyChanged("Generation");
            }
        }
    }
}